package com.feng.paper.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.feng.common.consts.ExamContant;
import com.feng.common.consts.PaperConst;
import com.feng.common.consts.QuTypeConstant;
import com.feng.common.enums.ApiErrorEnum;
import com.feng.common.exceptions.ServiceException;
import com.feng.common.vo.ApiRest;
import com.feng.exma.entity.Exam;
import com.feng.exma.entity.ExamRepo;
import com.feng.exma.mapper.ExamMapper;
import com.feng.exma.mapper.ExamRepoMapper;
import com.feng.paper.entity.Paper;
import com.feng.paper.entity.PaperQu;
import com.feng.paper.entity.PaperQuAnswer;
import com.feng.paper.mapper.PaperMapper;
import com.feng.paper.mapper.PaperQuAnswerMapper;
import com.feng.paper.mapper.PaperQuMapper;
import com.feng.paper.service.PaperService;
import com.feng.paper.vo.AnswerVo;
import com.feng.paper.vo.PaperVO;
import com.feng.paper.vo.QuDetail;
import com.feng.paper.vo.QuDetailVo;
import com.feng.security.entity.User;
import com.feng.security.mapper.UserMapper;
import com.feng.security.vo.UserVO;
import com.feng.timu.entity.QuAnswer;
import com.feng.timu.entity.QuRepo;
import com.feng.timu.mapper.QuAnswerMapper;
import com.feng.timu.mapper.QuRepoMapper;
import lombok.NonNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 试卷 服务实现类
 * </p>
 *
 * @author feng
 * @since 2023-03-23 20:30:41
 */
@Service
public class PaperServiceImpl extends ServiceImpl<PaperMapper, Paper> implements PaperService {
    @Autowired
    private ExamMapper examMapper;

    @Autowired
    private ExamRepoMapper examRepoMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private QuRepoMapper quRepoMapper;

    @Autowired
    private PaperQuMapper paperQuMapper;

    @Autowired
    private QuAnswerMapper quAnswerMapper;

    @Autowired
    private PaperQuAnswerMapper paperQuAnswerMapper;

    private static final List<String> ABC = Arrays.asList("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z");

    @Override
    @Transactional
    public ApiRest<String> awardPaper(String examId) {
        //校验考试是否存在
        Date nowTime = new Date();
        Exam exam = examMapper.selectById(examId);
        if (exam == null) {
            throw new ServiceException(ApiErrorEnum.EXAM_NOT_EXIT);
        }
        //校验是否在考试区间内
        if (exam.getStartTime() != null && exam.getEndTime() != null) {
            boolean hasExam = between(nowTime, exam.getStartTime(), exam.getEndTime());
            if (!hasExam) {
                throw new ServiceException(ApiErrorEnum.EXAM_NOT_IN_TIME);
            }
        }
        //校验考试是否结束
        if (exam.getState().equals(ExamContant.CLOSE)) {
            throw new ServiceException(ApiErrorEnum.EXAM_NOT_IN_TIME);
        }
        //校验试卷是否存在
        Object user = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (!(user instanceof UserVO)) {
            throw new ServiceException("非法登录");
        }
        UserVO userVO = (UserVO) user;
        QueryWrapper<Paper> paperQuery = new QueryWrapper<>();
        paperQuery.lambda().eq(Paper::getExamId, examId)
                .eq(Paper::getUserId, userVO.getUserId());
        Paper paper = baseMapper.selectOne(paperQuery);
        if (paper != null && paper.getState().equals(PaperConst.END)) {
            throw new ServiceException(ApiErrorEnum.EXAM_END);
        }
        if (paper != null && before(nowTime, paper.getLimitTime())) {
            return ApiRest.success(paper.getId());
        }
        if (paper != null && after(nowTime, paper.getLimitTime())) {
            throw new ServiceException("试卷作答时间已结束");
        }
        //生成试卷
        Paper newPaper = new Paper();
        //判断是否有简答题
        QueryWrapper<ExamRepo> examRepoQueryWrapper = new QueryWrapper<>();
        examRepoQueryWrapper.lambda()
                .select(ExamRepo::getJdtCount)
                .eq(ExamRepo::getExamId, examId);
        int hasJdt = 0;
        List<Integer> jdtList = examRepoMapper.selectObjs(examRepoQueryWrapper)
                .stream()
                .map(jdtCount -> (Integer) jdtCount)
                .collect(Collectors.toList());
        for (int i = 0; i < jdtList.size(); i++) {
            hasJdt += jdtList.get(i);
            //简答题数量不是0就是有简答题
            if (i == jdtList.size() - 1 && hasJdt != 0) {
                hasJdt = 1;
            }
        }
        //获取部门
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(User::getUserId, userVO.getUserId());
        User userDetail = userMapper.selectOne(queryWrapper);
        newPaper.setExamId(examId)
                .setUserId(userVO.getUserId().toString())
                .setTitle(exam.getTitle())
                .setUserTime(0)
                .setTotalTime(exam.getTotalTime())
                .setTotalScore(0)
                .setQualifyScore(exam.getQualifyScore())
                .setObjScore(0)
                .setSubjScore(0)
                .setUserScore(0)
                .setHasSaq(hasJdt)
                .setState(PaperConst.ING)
                //试卷创建时间+试卷规定时间=LimitTime
                .setLimitTime(new Date(nowTime.getTime() + exam.getTotalTime() * 60 * 1000))
                .setDepartId(userDetail.getDeptId().toString());
        baseMapper.insert(newPaper);
        //组卷，给卷子生成题目
        examRepoQueryWrapper = new QueryWrapper<>();
        examRepoQueryWrapper.lambda()
                .eq(ExamRepo::getExamId, examId);
        List<ExamRepo> examRepoList = examRepoMapper.selectList(examRepoQueryWrapper);
        //题目id
        List<String> quIdList = new ArrayList<>();
        int quNumId = 0;
        //单选
        for (ExamRepo examRepo : examRepoList) {
            List<QuRepo> radios = quRepoMapper.takeQu(examRepo.getRepoId(), QuTypeConstant.RADIO, examRepo.getRadioCount(), quIdList);
            //当前题库拿取的题目
            List<String> quList = radios.stream()
                    .map(QuRepo::getQuId)
                    .collect(Collectors.toList());
            quIdList.addAll(quList);
            for (String quid : quList) {
                PaperQu paperQu = new PaperQu();
                paperQu.setPaperId(newPaper.getId())
                        .setQuId(quid)
                        .setQuType(QuTypeConstant.RADIO)
                        .setAnswered(PaperConst.NOANSWERED)
                        .setAnswer("")
                        .setSort(++quNumId)
                        .setScore(examRepo.getRadioScore())
                        .setActualScore(0)
                        .setIsRight(PaperConst.WRONG);
                paperQuMapper.insert(paperQu);
            }
        }

        //多选
        for (ExamRepo examRepo : examRepoList) {
            List<QuRepo> multi = quRepoMapper.takeQu(examRepo.getRepoId(), QuTypeConstant.MULTI, examRepo.getMultiCount(), quIdList);
            //当前题库拿取的题目
            List<String> quList = multi.stream()
                    .map(QuRepo::getQuId)
                    .collect(Collectors.toList());
            quIdList.addAll(quList);
            for (String quid : quList) {
                PaperQu paperQu = new PaperQu();
                paperQu.setPaperId(newPaper.getId())
                        .setQuId(quid)
                        .setQuType(QuTypeConstant.MULTI)
                        .setAnswered(PaperConst.NOANSWERED)
                        .setAnswer("")
                        .setSort(++quNumId)
                        .setScore(examRepo.getMultiScore())
                        .setActualScore(0)
                        .setIsRight(PaperConst.WRONG);
                paperQuMapper.insert(paperQu);
            }
        }

        //判断
        for (ExamRepo examRepo : examRepoList) {
            List<QuRepo> judge = quRepoMapper.takeQu(examRepo.getRepoId(), QuTypeConstant.JUDGE, examRepo.getJudgeCount(), quIdList);
            //当前题库拿取的题目
            List<String> quList = judge.stream()
                    .map(QuRepo::getQuId)
                    .collect(Collectors.toList());
            quIdList.addAll(quList);
            for (String quid : quList) {
                PaperQu paperQu = new PaperQu();
                paperQu.setPaperId(newPaper.getId())
                        .setQuId(quid)
                        .setQuType(QuTypeConstant.JUDGE)
                        .setAnswered(PaperConst.NOANSWERED)
                        .setAnswer("")
                        .setSort(++quNumId)
                        .setScore(examRepo.getJudgeScore())
                        .setActualScore(0)
                        .setIsRight(PaperConst.WRONG);
                paperQuMapper.insert(paperQu);
            }
        }

        //客观题总数
        int optionNum = quNumId;

        //简答
        if (hasJdt == 1) {
            for (ExamRepo examRepo : examRepoList) {
                List<QuRepo> jdt = quRepoMapper.takeQu(examRepo.getRepoId(), QuTypeConstant.JDT, examRepo.getJdtCount(), quIdList);
                //当前题库拿取的题目
                List<String> quList = jdt.stream()
                        .map(QuRepo::getQuId)
                        .collect(Collectors.toList());
                quIdList.addAll(quList);
                for (String quid : quList) {
                    PaperQu paperQu = new PaperQu();
                    paperQu.setPaperId(newPaper.getId())
                            .setQuId(quid)
                            .setQuType(QuTypeConstant.JDT)
                            .setAnswered(PaperConst.NOANSWERED)
                            .setAnswer("")
                            .setSort(++quNumId)
                            .setScore(examRepo.getJdtScore())
                            .setActualScore(0)
                            .setIsRight(PaperConst.WRONG);
                    paperQuMapper.insert(paperQu);
                }
            }
        }

        //给客观题生成选项
        //i遍历qu
        //j遍历answer
        for (int i = 0; i < quNumId; i++) {
            QueryWrapper<QuAnswer> quAnswerQueryWrapper = new QueryWrapper<>();
            quAnswerQueryWrapper.lambda()
                    .eq(QuAnswer::getQuId, quIdList.get(i));
            if (i < optionNum) {
                List<QuAnswer> quAnswerList = quAnswerMapper.selectList(quAnswerQueryWrapper);
                for (int j = 0; j < quAnswerList.size(); j++) {
                    PaperQuAnswer paperQuAnswer = new PaperQuAnswer();
                    paperQuAnswer
                            .setPaperId(newPaper.getId())
                            .setQuId(quIdList.get(i))
                            .setAnswerId(quAnswerList.get(j).getId())
                            .setChecked(0)
                            .setIsRight(quAnswerList.get(j).getIsRight())
                            .setAbc(ABC.get(j));
                    paperQuAnswerMapper.insert(paperQuAnswer);
                }
            } else {
                PaperQuAnswer paperQuAnswer = new PaperQuAnswer();
                paperQuAnswer
                        .setPaperId(newPaper.getId())
                        .setQuId(quIdList.get(i))
                        .setAnswerId("")
                        .setChecked(0)
                        .setIsRight(PaperConst.WRONG);
                paperQuAnswerMapper.insert(paperQuAnswer);
            }

        }
        return ApiRest.success(newPaper.getId());
    }

    @Override
    public ApiRest<PaperVO> returnPaper(String paperId) {
        LambdaQueryChainWrapper<PaperQu> lambdaQueryChainWrapper =
                new LambdaQueryChainWrapper<>(paperQuMapper);
        PaperVO paperVO = new PaperVO();
        List<PaperQu> quList = lambdaQueryChainWrapper
                .eq(PaperQu::getPaperId, paperId)
                .list();
        paperVO.setRadios(quList
                .stream()
                .filter(qu -> qu.getQuType().equals(QuTypeConstant.RADIO))
                .collect(Collectors.toList()));
        paperVO.setMultis(quList
                .stream()
                .filter(qu -> qu.getQuType().equals(QuTypeConstant.MULTI))
                .collect(Collectors.toList()));
        paperVO.setJudges(quList
                .stream()
                .filter(qu -> qu.getQuType().equals(QuTypeConstant.JUDGE))
                .collect(Collectors.toList()));
        paperVO.setSaqs(quList
                .stream()
                .filter(qu -> qu.getQuType().equals(QuTypeConstant.JDT))
                .collect(Collectors.toList()));
        LambdaQueryChainWrapper<Paper> paperLambdaQueryChainWrapper =
                new LambdaQueryChainWrapper<>(baseMapper);
        Paper paper = paperLambdaQueryChainWrapper.eq(Paper::getId, paperId).one();
        long limitTime = paper.getLimitTime().getTime();
        paperVO.setLeftSeconds((limitTime - System.currentTimeMillis()) / 1000);
        return ApiRest.success(paperVO);
    }

    @Override
    public ApiRest<QuDetailVo> quDetail(String paperId, String quId) {
        Paper paper = baseMapper.selectById(paperId);
        if (paper == null) {
            throw new ServiceException(ApiErrorEnum.PAPER_NOT_EXIT);
        }
        QuDetailVo quDetailVo = new QuDetailVo();
        //查询题目content
        QuDetail quInfo = paperQuMapper.findQuInfo(paperId, quId);
        if (quInfo == null) {
            throw new ServiceException(ApiErrorEnum.PAPER_QU_NOT_EXIT);
        }
        quDetailVo.setQuDetail(quInfo);
        List<AnswerVo> answer = quAnswerMapper.findAnswer(paperId, quId);
        quDetailVo.setAnswers(answer);
        return ApiRest.success(quDetailVo);
    }

    @Override
    @Transactional
    public ApiRest<Integer> handPaper(String paperId) {
        Paper paper = baseMapper.selectById(paperId);
        if (paper == null) {
            throw new ServiceException(ApiErrorEnum.PAPER_NOT_EXIT);
        }
        Integer score = baseMapper.score(paperId);
        long userTime = paper.getLimitTime().getTime() - System.currentTimeMillis() / 1000 * 60;
        paper.setState(PaperConst.END)
                .setObjScore(score)
                .setUserTime((int) userTime);
        baseMapper.updateById(paper);

        return ApiRest.success(score);
    }

    /**
     * 如果当前时间等于开始时间或结束时间，则返回true；否则比较当前时间是否在开始时间和结束时间之间，如果是，则返回true，否则返回false。
     *
     * @param nowTime   当前时间
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @return
     */
    private boolean between(Date nowTime, Date startTime, Date endTime) {
        if (nowTime.getTime() == startTime.getTime()
                || nowTime.getTime() == endTime.getTime()) {
            return true;
        }
        Calendar date = Calendar.getInstance();
        Calendar begin = Calendar.getInstance();
        Calendar end = Calendar.getInstance();
        date.setTime(nowTime);
        begin.setTime(startTime);
        end.setTime(endTime);
        return date.after(begin) && date.before(end);
    }

    private boolean after(@NonNull Date nowTime, @NonNull Date time) {
        Calendar date = Calendar.getInstance();
        Calendar timeDate = Calendar.getInstance();
        date.setTime(nowTime);
        timeDate.setTime(time);
        return date.after(timeDate);
    }

    private boolean before(@NonNull Date nowTime, @NonNull Date time) {
        Calendar date = Calendar.getInstance();
        Calendar timeDate = Calendar.getInstance();
        date.setTime(nowTime);
        timeDate.setTime(time);
        return date.before(timeDate);
    }
}
